Create Mass Property Description


Alternative way to generate MEL. Using a “Work Package” viewpoint to aggregate the total mass of supplying components of authorships
Author

Yuta Nakajima

Published

March 16, 2024

1 Workflow

?@sec-introduction

flowchart LR
  A([Run Fuseki]) --> B([Query Data])
  B --> C((Tidy Data))
  C --> D([Tree: WP])
  C --> E([Tree: Configuration])
  C --> F([Table: Mass Property])
  D --> G([Decomposition Tree])
  E --> G
  G --> H([Roll up Calculations])
  F --> H
  H --> I([Report])
  

System Decomposition12

[1] "/Users/mlab/Workspaces/github/open-source-rover/"

1.1 Reusable function to Visualize Tree

Code
# I want to plot graph as dendrogram. graph doesnt have NA root for plotCollapsibleTreeFromDataframe
plotGraph2Dendrogram <- function(g){

  # find root nodes
  root_wp <- which(degree(g, mode = "in") == 0)
  root_wp <- names(root_wp)

  # Add NA
#  g <- add_vertices(g, 1, name="NA")
#  g <- add_edges(g, c("NA", root_wp))

  df <- igraph::as_data_frame(g, what = "edges") %>%
    add_row(from=NA, to=root_wp)
  
  
  g2 <- graph_from_data_frame(df, 
                           directed = TRUE, 
                           vertices = NULL)

  df_deg <- data.frame(
    name = names(degree(g2)),
    degree = degree(g2),
    distance = distances(g2)["NA",]
  )
  
  df_g <- df %>%
    left_join(df_deg, by=c("to"="name")) 
  
  p<-plotCollapsibleTreeFromDataframe(df_g, palette="BluYl", parent="from", child="to",type="distance")
  
  
  return(p)
}

2 Run Fuseki

2.1 Build and Run a Fuseki SPARQL endpoint for oml model query

Code
library(omlhashiR)
## oml_repository <- "../open-source-rover/"
oml_repository <- omlrepo
# omlhashiR::oml_refresh()
# omlhashiR::oml_stop_Daemon(oml_repository)
# omlhashiR::oml_build(oml_repository)
# omlhashiR::oml_startFuseki(oml_repository)
# omlhashiR::oml_owlLoad(oml_repository)

3 Query Data

Code
library(tansakusuR)
endpoint_url <- "http://localhost:3030/open-source-rover/sparql"
repo <- paste0(omlrepo, "src/vision/sparql/")

3.1 Tree: Configuration

Code
### <TBD> Analysis: Read Current Mass Property Data in Model

#order <- dfs(g, V(g)[root], order.out = TRUE)$order

file <- "node_systemassemblymass.sparql"
filepath <- paste0(repo,file)
#show_query(filepath)
df_mass <- send_query_from_file(endpoint_url, filepath) %>%
  mutate(mass = replace_na(as.numeric(m),0)) 

df_configuration <- df_mass %>%
  select(parent, id, name, type, mass, iri, owner)

df_mass2 <- df_configuration

3.2 Visualize Configuration

This might be generated by model query directly. Because we use system-subsystem-assembly configurations now, we produce this dataframe manually.

Code
df_config<- df_configuration %>%
  mutate(container="OSR") %>%
  mutate(name=paste0(parent)) %>%
  mutate(edgetype=paste0("contains")) %>%
  select("container","name","type") %>%
  filter(container != name)

#df_config$owner[df_config$name == "OSR"] <- NA

g2 <- graph_from_data_frame(df_config, 
                           directed = TRUE, 
                           vertices = NULL)

plot(g2, layout=layout_as_tree)

Code
plotGraph2Dendrogram(g2)

3.3 Tree: WP authorizes WP

Components are grouped using Work Package Tree, and mass property data is aggregated by this grouping strategy.

Code
file <- "edge_workpackage.sparql"
filepath <- paste0(repo,file)
#show_query(filepath)
df_wp <- send_query_from_file(endpoint_url, filepath)
# datatable(df_wp, options = list(pageLength = -1))]
df_wp
# A tibble: 11 × 4
   parent                 child                              componentType iri  
   <chr>                  <chr>                              <chr>         <chr>
 1 wp-system              wp-CDHSubsystem                    http://imce.… http…
 2 wp-system              wp-ControlSubsystem                http://imce.… http…
 3 wp-system              wp-MechanicalSubsystem             http://imce.… http…
 4 wp-system              wp-MobilitySubsystem               http://imce.… http…
 5 wp-system              wp-NavigationSubsystem             http://imce.… http…
 6 wp-system              wp-PowerSubsystem                  http://imce.… http…
 7 wp-MechanicalSubsystem wp-body-assembly                   http://imce.… http…
 8 wp-MobilitySubsystem   wp-corner-wheel-assembly           http://imce.… http…
 9 wp-MobilitySubsystem   wp-drive-wheel-assembly            http://imce.… http…
10 wp-MechanicalSubsystem wp-mechanical-harness              http://imce.… http…
11 wp-MechanicalSubsystem wp-rocker-bogie-suspension-assemb… http://imce.… http…

3.4 WP

Code
df2<- df_wp %>%
  mutate(owner=paste0(parent)) %>%
  mutate(name=paste0(child)) %>%
  mutate(type=paste0("authorizes")) %>%
  select("owner","name","type")

g2 <- graph_from_data_frame(df2, 
                           directed = TRUE, 
                           vertices = NULL)

# plotGraph2Dendrogram(g2)
plot(g2, layout=layout_as_tree)

3.5 WP Supplies Components

Code
df_sc <- df_mass %>%
  select(parent, id, name, type, mass, iri, owner) %>%
  mutate(child = parent) %>%
  mutate(parent = owner) %>%
  mutate(childiri = iri) %>%
  mutate(edgetype = "supplies") %>%
  select(parent, child, edgetype)

3.6 Analysis: Create Containment Graph

Code
root <- "OSR"

df_wp <- df_wp %>%
  mutate(edgetype = "authorizes") %>%
  select(parent, child, edgetype)


df <- rbind(df_wp, df_sc)

# swap parent and child of root
index <- which(df$child==root)
c<-df$child[index]
p<-df$parent[index]
df$child[index]<-p
df$parent[index]<-c

g <- graph_from_data_frame(df[,c("parent","child")], 
                           directed = TRUE, 
                           vertices = NULL)


plot(g, layout=layout_as_tree)

3.7 Tree: WP supplies Component

Code
file <- "edge_suppliedComponents.sparql"
filepath <- paste0(repo,file)
#show_query(filepath)
df_c <- send_query_from_file(endpoint_url, filepath)
# datatable(df_c, options = list(pageLength = -1))
df_c
# A tibble: 11 × 4
   parent                              child                 componentType iri  
   <chr>                               <chr>                 <chr>         <chr>
 1 wp-body-assembly                    body-assembly         http://imce.… http…
 2 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 3 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 4 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 5 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 6 wp-drive-wheel-assembly             drive-wheel-assembly… http://imce.… http…
 7 wp-drive-wheel-assembly             drive-wheel-assembly… http://imce.… http…
 8 wp-mechanical-harness               mechanical-harness    http://imce.… http…
 9 wp-rocker-bogie-suspension-assembly rocker-bogie-suspens… http://imce.… http…
10 wp-rocker-bogie-suspension-assembly rocker-bogie-suspens… http://imce.… http…
11 wp-system                           OSR                   http://imce.… http…

ここまではOK。ただしサブシステムの情報を組み込んだMELのテンプレを新たに用意する必要がある。 マスロールアップは、サブシステムを含むgraphデータを探索することになるので、グラフデータを起点にした方が良いかもしれない。 例えば、あるコンフィグレーションの node_systemassemblymass.sparql を実行する。 これとワークパッケージを対応づける必要がある。

Code
order <- dfs(g, V(g)[root], order.out = TRUE)$order

df_mel <- igraph::as_data_frame(g, what = "vertices") %>%
  arrange(factor(name, levels = names(order)))

df_mel2 <- left_join(df_mel, df_mass2, by = c("name"="parent"))

この処理の問題点は、owner情報が失われている点である。

  • edge_workpackage.sparql
  • edge_suppliedComponents.sparql
  • node_systemassemblymass.sparql

様々なコンフィグレーションに対応するためにも、 System -> Assemblyのコンフィグレーションを取り出す必要がある。 現状は、node_systemassemblymass.sparql の処理がそれに該当する。

Code
df_wp
# A tibble: 11 × 3
   parent                 child                               edgetype  
   <chr>                  <chr>                               <chr>     
 1 wp-system              wp-CDHSubsystem                     authorizes
 2 wp-system              wp-ControlSubsystem                 authorizes
 3 wp-system              wp-MechanicalSubsystem              authorizes
 4 wp-system              wp-MobilitySubsystem                authorizes
 5 wp-system              wp-NavigationSubsystem              authorizes
 6 wp-system              wp-PowerSubsystem                   authorizes
 7 wp-MechanicalSubsystem wp-body-assembly                    authorizes
 8 wp-MobilitySubsystem   wp-corner-wheel-assembly            authorizes
 9 wp-MobilitySubsystem   wp-drive-wheel-assembly             authorizes
10 wp-MechanicalSubsystem wp-mechanical-harness               authorizes
11 wp-MechanicalSubsystem wp-rocker-bogie-suspension-assembly authorizes
Code
df_c
# A tibble: 11 × 4
   parent                              child                 componentType iri  
   <chr>                               <chr>                 <chr>         <chr>
 1 wp-body-assembly                    body-assembly         http://imce.… http…
 2 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 3 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 4 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 5 wp-corner-wheel-assembly            corner-wheel-assembl… http://imce.… http…
 6 wp-drive-wheel-assembly             drive-wheel-assembly… http://imce.… http…
 7 wp-drive-wheel-assembly             drive-wheel-assembly… http://imce.… http…
 8 wp-mechanical-harness               mechanical-harness    http://imce.… http…
 9 wp-rocker-bogie-suspension-assembly rocker-bogie-suspens… http://imce.… http…
10 wp-rocker-bogie-suspension-assembly rocker-bogie-suspens… http://imce.… http…
11 wp-system                           OSR                   http://imce.… http…
Code
df_mass
# A tibble: 11 × 8
   parent                             id    name   m     type  owner iri    mass
   <chr>                              <chr> <chr>  <chr> <chr> <chr> <chr> <dbl>
 1 body-assembly                      A.01  Body … 30.0  Asse… wp-b… http…    30
 2 drive-wheel-assembly-1             A.02  Drive… 15.0  Asse… wp-d… http…    15
 3 drive-wheel-assembly-2             A.03  Drive… 15.0  Asse… wp-d… http…    15
 4 corner-wheel-assembly-1            A.04  Corne… 25.0  Asse… wp-c… http…    25
 5 corner-wheel-assembly-2            A.05  Corne… 25.0  Asse… wp-c… http…    25
 6 corner-wheel-assembly-3            A.06  Corne… 25.0  Asse… wp-c… http…    25
 7 corner-wheel-assembly-4            A.07  Corne… 25.0  Asse… wp-c… http…    25
 8 rocker-bogie-suspension-assembly-1 A.08  Rocke… 40.0  Asse… wp-r… http…    40
 9 rocker-bogie-suspension-assembly-2 A.09  Rocke… 40.0  Asse… wp-r… http…    40
10 mechanical-harness                 A.10  Mecha… 50.0  Asse… wp-m… http…    50
11 OSR                                C.01  Rover… 1640… Syst… wp-s… http…  1640

3.8 wp tree + “wp supplies components”

Code
df_wp_2<- df_wp %>%
  mutate(owner=paste0(parent)) %>%
  mutate(name=paste0(child)) %>%
  mutate(type=paste0("authorizes")) %>%
  select("owner","name","type")

df_c_2<- df_c %>%
  mutate(owner=paste0(parent)) %>%
  mutate(name=paste0(child)) %>%
  mutate(type=paste0("supplies")) %>%
  select("owner","name","type")

df2<- rbind(df_wp_2, df_c_2)
df_g <- df2  
# df_g <- df2 %>% 
#   add_row(owner=NA, name="wp-system", type="root")

# 
# df2<- rbind(df_wp, df_c) %>%
#   mutate(owner=paste0(parent)) %>%
#   mutate(name=paste0(child)) %>%
#   mutate(type=paste0("Component")) %>%
#   select("owner","name","type") %>%
#   add_row(owner=NA, name="wp-system", type="root")

g2 <- graph_from_data_frame(df_g, 
                           directed = TRUE, 
                           vertices = NULL)


plot(g2, layout=layout_as_tree)

Code
plotGraph2Dendrogram(g2)

4 Analysis: Decomposition Tree

Set a “System” as a root node of the tree. Remove a node of “system isSuppliedBy Work Package”

Code
# find root nodes
root_wp <- which(degree(g2, mode = "in") == 0)
root_wp <- names(root_wp)

root_nodes <- df_mass$parent[df_mass$owner == root_wp]

# Replace root_wp as root_nodes
df2[df2 == root_wp] <- root_nodes

# Remove node
df2 <- df2 %>% 
  filter(owner != name)

#df2$owner[df2$name == root_nodes] <- NA

  
g3 <- graph_from_data_frame(df2, 
                           directed = TRUE, 
                           vertices = NULL)

plot(g3, layout=layout_as_tree)

Code
plotGraph2Dendrogram(g3)

5 Currently below subsystems miss assemblies with mass data

Code
order <- dfs(g3, V(g3)[root], order.out = TRUE)$order

df_mel <- igraph::as_data_frame(g3, what = "vertices") %>%
  arrange(factor(name, levels = names(order)))%>%
  filter(name !="NA")


df_mel2 <- left_join(df_mel, df_mass2, by = c("name"="parent"))
Code
df_mel2$mass[df_mel2$name=="wp-CDHSubsystem"] <- 100.0
df_mel2$mass[df_mel2$name=="wp-ControlSubsystem"] <- 200.0
df_mel2$mass[df_mel2$name=="wp-NavigationSubsystem"] <- 300.0
df_mel2$mass[df_mel2$name=="wp-PowerSubsystem"] <- 400.0
Code
namekey="name"
masskey="mass"

df_mass_update <- massRollUp(g3, root, df_mel2, namekey = "name",masskey = "mass")
Code
df_table <- df_mass_update %>%
  select(name, mass) %>%
  left_join(select(df_g, name, owner, type), by=c("name"="name"))

df_table$owner[1] <- df_table$name[1]
Code
library(reactable)
reactable(df_table, groupBy = "owner")

6 Update Mass Properties using Json

This process includes user interface of the VS-Code Extension.

json editor

7 Read Mass Properties from data

Code
#path = "./data_massproperty.json"
#path = "./quarto_docs/chapters/04_massrollup/data_massproperty.json"
# path <- searchDirectory(4, "data_massproperty.json", dirname(getwd()))

# df_json <- read_json(path = path,  simplifyVector = TRUE)

8 Analysis: Mass Rollup By Depth-First Traversal

Code
# df_mass_update <- massRollUp(g, root, df_json)

9 Compare: Current OML Descriptions vs JSON

Code
# df_mass_compare <- left_join(df_mass_update, df_mass, by = c("c1_localname","c1_id","c1_name","c1_type"), suffix = c("", "_before"))
# df_mass_compare

10 Generate OML Descriptions

10.1 Create Instance

Code
# df_instance <- data.frame(
#   name = df_mass_update$c1_localname,
#   instancename = paste0(df_mass_update$c1_localname,".mass.magnitude"),
#   mass = df_mass_update$c1_mass,
#   type = str_replace_all(df_mass_update$c1_type, c("System"="subsystems", "Subsystem"="subsystems", "Assembly"="assembly"))
#   ) 

10.2 Generate OML Mass Descriptions

Code
# outputdir <- paste0(omlrepo,"src/oml/opencaesar.io/open-source-rover/description/mass/")
# outputfile <- paste0(outputdir, "masses.oml")
# omldescriptions <- generateOmlMassDescriptions(df_instance)

10.3 Generate OML File

Code
# cat(file=outputfile, omldescriptions)

11 Update Json

Code
# write_json(df_mass_update, path = path, pretty=TRUE)
Code
# df2<- df %>%
#   mutate(c2_mass = df$c1_mass[match(unlist(df$c2_localname), df$c1_localname)]) %>%
#   mutate(owner=paste0(c2_localname," (", c2_mass," kg)")) %>%
#   mutate(name=paste0(c1_localname," (", c1_mass," kg)")) %>%
#   select("owner","name","c1_type")
# 
# # Set NA node for CollapsibleTree
# df2$owner[df2$owner=="NA (NA kg)"] <- NA
# 
# 
# plotCollapsibleTreeFromDataframe(df2, palette="BluYl", parent="owner", child="name",type="c1_type")

11.1 Some visualization experiments

networkD3::simpleNetwork

Code
# df2<- df %>% 
#   mutate(owner=parent) %>%
#   mutate(name=child) %>%
#   select("owner","name") %>%
#   arrange(desc(owner))   
# 
# 
# library(networkD3)
# 
# networkD3::simpleNetwork(df2)